
grammar martino;

@header {
	
	import java.lang.String;
	import java.util.ArrayList;
}

@rulecatch {
	catch (RecognitionException e) {
		reportError(e);
		System.exit(1);
	}
}
@members {

	public void emitErrorMessage(String mensaje)
	{
		System.err.println(mensaje);
		System.exit(2);
	}
	
	SymbolTable table = new SymbolTable();
	Combinations combinations = new Combinations();
	NoteManager noteManager = new NoteManager();
	PortVector ports = new PortVector();
	int defaultChannel = 0;
	boolean polyphonic = false;
}

s returns [String tcode]
	@init{
		ports.init();
	}: monopoly INSTRUMENT {
		$tcode = "/* Translated code. Martino project */ \n";
			
	} instr{
		$tcode += "/* " + $monopoly.tcode + " INSTRUMENT " + $instr.nameInstrument +  " */ \n";      
		$tcode += $instr.tcode;
		System.out.println($tcode);
	}
	;

monopoly returns [String tcode]
	:	MONOPHONIC {
		$tcode = "MONOPHONIC"; 
		polyphonic = false;
	} | POLYPHONIC {
		$tcode = "POLYPHONIC"; 
		polyphonic = true;
	} ;
instr returns [String tcode, String nameInstrument]	
	@init{
		$tcode = "";
	}:	
	ID COLON e1=elems{
		
		$nameInstrument = $ID.text;
		
	} effect[$e1.readingDigitals] {
	
		$tcode = Strings.defines;
		if(polyphonic){
			$tcode += Strings.noteVectorsPoly;
		} else {
			$tcode += Strings.noteVectorsMono;
		}
		$tcode += $effect.stateDeclarations;
		$tcode += $e1.tcode;
		$tcode += $effect.tcode;
	} DOT
	| 
	;
	
	
effect[String readingDigitals] 	returns [String tcode, String stateDeclarations]: GENERATORS COLON generators{
		$tcode = "void loop(){\n";
		
		$tcode += $generators.tcode;
		$tcode += readingDigitals; 
		$stateDeclarations = $generators.stateDeclarations; 
		
	} DOT smodifiers{
		$tcode += $smodifiers.tcode;			
		$tcode += "\n}\n";
	}
	| {
		$tcode = "void loop(){\n";
		$tcode += readingDigitals;
		$tcode += "}\n";
	}
	;
	elems 	returns [String tcode, String readingDigitals]
	: ELEMS COLON gelements{
		$tcode = $gelements.tcode;
		$readingDigitals = $gelements.readingDigitals;
	} DOT
	| {
		$tcode = "";
		$readingDigitals="";
	}	
	;

gelements returns [String tcode, String readingDigitals] 
	: el{
		$tcode = $el.tcode;
		$readingDigitals = $el.readingDigitals;
	
	} gelements2 {
		
		
		$tcode += $gelements2.tcode;
		$readingDigitals+=$gelements2.readingDigitals;
		
		
		
		$tcode += Strings.noteOn;
		$tcode += Strings.noteOff;
		
		if(polyphonic){
			$tcode += Strings.playNotePoly;
			$tcode += Strings.silenceNotePoly;
			$tcode += Strings.soundOffPoly;
		} else {
			$tcode += Strings.soundOffMono;
			$tcode += Strings.playNoteMono;	
		}
  		$tcode += Strings.pitchBend;
		$tcode += "void setup(){\n";
		for(int i=0; i< ports.numDigitals; i++){
			if(ports.digitalVector[i] == 1){
				$tcode+= "\tpinMode("+Integer.toString(i)+",INPUT);\n";
			}
		}
		if(polyphonic){
			$tcode += Strings.setupPoly;
		} else {
			$tcode += Strings.setupMono;
		}
		$tcode += "}\n\n";
	};

gelements2 returns [String tcode,String readingDigitals]
	@init{
		$readingDigitals = "";
		$tcode = "";
	}
	: COMMA el g2=gelements2 {
		$tcode = $el.tcode;
		$tcode += $g2.tcode;
		$readingDigitals = $el.readingDigitals;
		$readingDigitals+= $g2.readingDigitals;
		
	}
	|  
	;
	 
el 	returns [String tcode, String readingDigitals]
	@init{
		Symbol element;
		$readingDigitals = "";
		$tcode = "";
	}: sensor CONNECTED port{
			if(!ports.on($port.text))
					//Port used
			} 
		values{  
			$tcode = "int " + $sensor.name + ";\n";
			if($port.digital){
				$readingDigitals+="\t"+$sensor.name+ "=digitalRead("+$port.portn+");\n";
			}
		if($values.withValues){
		  element = new Symbol($sensor.name, $sensor.type,$port.portn, $values.min, $values.max);
		} else {
		  element = new Symbol($sensor.name, $sensor.type,$port.portn);
		}
		table.addSymbol(element);
	};

port	returns [String portn, boolean digital]: 
	INT{
		$portn = $INT.text; 
		$digital = true;
	}
	| AINT{
		$portn = $AINT.text; 
		$digital = false;
	}
	;
	
sensor 	returns [String name, Symbol.type type]:
		ID{
			$name = $ID.text;
			$type = Symbol.type.flow;
		};

values 	returns [String tcode, int min, int max, boolean withValues]
	: PARI i1=INT COMMA i2=INT PARD{
		$withValues = true;
		$min = Integer.parseInt($i1.text);
		$max = Integer.parseInt($i2.text);	
	}	
	| {	$withValues = false;
		
	}
	;



generators returns [String tcode, String stateDeclarations] 
	@init{
		$tcode = "";
		$stateDeclarations = "";
	
	}
	
	: generator generators2{
		$tcode = $generator.tcode;
		$tcode += $generators2.tcode;
		$stateDeclarations = $generator.stateDeclarations + $generators2.stateDeclarations;
	}
	| {
		$tcode="";
		$stateDeclarations = "";
	}
	;

generators2 returns [String tcode, String stateDeclarations]
	@init{
		$stateDeclarations = "";
		$tcode = "";
	
	}
	: g1=generator g2=generators2{
		
		$tcode = $g1.tcode;
		$tcode += $g2.tcode;
		$stateDeclarations = $g1.stateDeclarations + $g2.stateDeclarations;
	}
	| {
		$tcode="";
		$stateDeclarations = "";
	}
	;
	
generator returns [String tcode, String stateDeclarations]
	@init{
		Symbol sgenerator;
		$tcode = "";
		$stateDeclarations = "";
	}	
	: ID{
		sgenerator = table.get($ID.text);
		if(sgenerator==null){
			sgenerator = new AlwaysTrueSymbol($ID.text,Symbol.type.pushbutton);
			table.addSymbol(sgenerator);
			
		}
	}channeloverride[sgenerator] mapping[$ID.text]{
	
		if(sgenerator.getClass()!=AlwaysTrueSymbol.class){
			$stateDeclarations = "int _"+$ID.text+";\n"; 	
			if(!$mapping.bending){	
				if(!$mapping.withValues){
					$tcode = "\t_"+$ID.text+"=analogRead("+sgenerator.port+");\n";
				} else {
					$tcode = "\t_"+$ID.text+"=map(analogRead("+sgenerator.port+"),"+String.valueOf(sgenerator.minValue)+
						","+String.valueOf(sgenerator.maxValue)+","+$mapping.min+","+$mapping.max+");\n";
				}				
			}
			$tcode += $mapping.tcode;
			$stateDeclarations += $mapping.stateDeclarations;
		}
	};
	
	
channeloverride[Symbol sgenerator]
	@init{
		Symbol aux = $sgenerator;
	}: CHANNEL INT{	
		aux.channel = Integer.parseInt($INT.text);
	} | {
		aux.channel = defaultChannel;
	}
	
	;
	

mapping[String name]	returns [String tcode, int min, int max, boolean withValues, String stateDeclarations, boolean bending]
	: values{
		$bending = false;
		$min = $values.min;
		$max = $values.max;
		$withValues = $values.withValues;
		
	} STATES COLON mvalues[$name,0]{
		$tcode = $mvalues.tcode;
		$stateDeclarations = $mvalues.stateDeclarations;
		
	} DOT
	| PITCHES DOT{
		$bending = true;
		$tcode = "";
		Symbol bender = table.get(name);
		if(bender!=null){
			$tcode += "\n\t_"+bender.name+" = map(analogRead(" + bender.port +"),"+String.valueOf(bender.minValue)+","+ String.valueOf(bender.maxValue) +",0,16383);\n";
			$tcode += "\tif(_"+bender.name+"!="+bender.name+"){\n";
			$tcode += "\t\t"+bender.name + "=_"+bender.name+";\n";
			$tcode += "\t\tpitchbend("+bender.channel+","+bender.name+");\n";
			$tcode += "\t}\n";
		}
		$withValues = false;
		$stateDeclarations = "";
	
	}	
	| {
		$tcode = "";
		$stateDeclarations = "";
	}
	;

mvalues[String name, int numState] returns [String tcode, String stateDeclarations]: mvalue[$name,$numState] mvalues2[$name,$numState]{
		$tcode = $mvalue.tcode;
		$stateDeclarations = $mvalue.stateDeclaration;
		if($mvalues2.tcode!=""){
			$tcode += "\telse ";
			$tcode += $mvalues2.tcode;
			$stateDeclarations += $mvalues2.stateDeclarations;

		}
	};
mvalues2[String name, int numState] returns [String tcode, String stateDeclarations] : COMMA mvalue[$name, $numState+1] m2=mvalues2[name, $numState+1]{
		$tcode = $mvalue.tcode;
		$stateDeclarations = $mvalue.stateDeclaration;
		if($m2.tcode!=""){
			$tcode += "else ";
			$tcode += $m2.tcode;
			$stateDeclarations += $m2.stateDeclarations;	
		}
	}
	| {
		$tcode ="{\n\t\t"+ name + "= -1; \n\t}\n";
		$stateDeclarations = "";
	}
	;

mvalue[String name, int numState] returns [String tcode, String stateDeclaration]: 
	FROM i1=INT TO i2=INT mvalueend{
		$stateDeclaration = "";
		if($mvalueend.isAState){
			$stateDeclaration = "int " + $name + "_" + $mvalueend.stateName + "=" + String.valueOf(numState)+";\n";
			$tcode = "\tif(" + "_"+$name+">="+Math.min(Integer.parseInt($i1.text), Integer.parseInt($i2.text));
			$tcode += "&&"+"_"+$name+"<="+Math.max(Integer.parseInt($i1.text), Integer.parseInt($i2.text))+"){\n";
			$tcode += "\t\t"+$name+"="+ $name + "_" + $mvalueend.stateName+";\n\t}";
			
		}
	
	};

mvalueend returns [String stateName, boolean isAState]
	: STATE mvid{
		$isAState = true;
		$stateName = $mvid.id;
	
	}
	;
	
mvid 	returns [String id]: INT {
			$id = String.valueOf($INT.text);
	}
	|ID {
			$id = $ID.text;
	};
	
smodifiers returns [String tcode]
	@init{
		$tcode = "";
	}
	: MODIFIERS COLON modifiers DOT {
		String elseTxt = "";
		Symbol generator = null;


		$tcode = "\n\t";
		if(!polyphonic){		

			if(combinations.elementCombinations.size()>0){
				combinations.sort();
				for (Combination c: combinations.elementCombinations){
					$tcode+=elseTxt;
					$tcode+="if(";
					for(String s: c.elements){
						$tcode+=s+"==HIGH&&";
					}
					generator = table.get(c.attachedSymbol.name);
					if(generator.getClass()!=AlwaysTrueSymbol.class){
						
						$tcode += c.attachedSymbol.name +"=="+c.attachedSymbol.name+"_"+c.attachedState;
					} else{
						$tcode = $tcode.substring(0,$tcode.length()-2);
					}
					$tcode += "){\n";
					$tcode += "\t\tplayNoteMono("+ generator.channel + "," + noteManager.get(c.note)+",0x45);\n";
					elseTxt = "\t} else ";
				}
				$tcode+="\t} else {\n\t\tsoundOffMono("+generator.channel+");\n\t}";
				
			} 
		} else {
			if(combinations.elementCombinations.size()>0){
				combinations.sort();
				for (Combination c: combinations.elementCombinations){
					$tcode+="if(";
					for(String s: c.elements){
						$tcode+=s+"==HIGH&&";
					}
					generator = table.get(c.attachedSymbol.name);
					if(generator.getClass()!=AlwaysTrueSymbol.class){
						
						$tcode += c.attachedSymbol.name +"=="+c.attachedSymbol.name+"_"+c.attachedState;
					} else{
						$tcode = $tcode.substring(0,$tcode.length()-2);
					}
					$tcode += "){\n";
					$tcode += "\t\tplayNotePoly("+ generator.channel + "," + noteManager.get(c.note)+",0x45);\n";
					$tcode += "\t} else {\n\t\tsilenceNotePoly("+generator.channel+"," + noteManager.get(c.note)+",0x45);\n\t}\n\t";
				}				
			} 
		}
	}
	| 
	;

modifiers returns [String pitchbend]
	: modifier modifiers2{
		$pitchbend = $modifier.pitchbend + $modifiers2.pitchbend;
	}
	|{
		$pitchbend = "";
	}
	;

modifiers2 returns [String pitchbend]
	: COMMA modifier m2=modifiers2{
		$pitchbend = $modifier.pitchbend + $m2.pitchbend;
	}
	| {
		$pitchbend = "";
	}
	;
	
modifier returns [String pitchbend]
	: group SOUNDS lmspecs{
		$pitchbend = "";
		int numcombinations = $lmspecs.notes.size();
		Combination comb;
		for(int i=0; i< numcombinations; i++){
			comb = $group.comb.clone();
			comb.note = $lmspecs.notes.get(i);
			comb.attachedSymbol = $lmspecs.generatorsl.get(i);
			comb.attachedState = $lmspecs.states.get(i);
			combinations.elementCombinations.add(comb);
		}
	};
	
group	returns [Combination comb]: ID group2{
		$comb = $group2.comb;
		$comb.elements.add($ID.text);
	}
	| DEFAULT {
		$comb = new Combination();
		//$comb.elements.add($DEFAULT.text);
	}
	;

group2 returns [Combination comb]: AND ID g2=group2 {
	$comb = $g2.comb;
	$comb.elements.add($ID.text);
	}
	| {
		$comb = new Combination();
	}
	;
	
lmspecs returns [ArrayList<String> notes, ArrayList<Symbol> generatorsl, ArrayList<String> states]: prespec lmspecs2{

		$notes = $lmspecs2.notes;
		$generatorsl = $lmspecs2.generatorsl;
		$states = $lmspecs2.states;	
		$notes.add(0,$prespec.note);
		$generatorsl.add(0,$prespec.sgenerator);
		$states.add(0,$prespec.state);

	};

lmspecs2 returns [ArrayList<String> notes, ArrayList<Symbol> generatorsl, ArrayList<String> states] : AND prespec l2=lmspecs2{
		$notes = $l2.notes;
		$generatorsl = $l2.generatorsl;
		$states = $l2.states;
		
		$notes.add(0,$prespec.note);
		$generatorsl.add(0,$prespec.sgenerator);
		$states.add(0,$prespec.state);
	} 
	| {
		$notes = new ArrayList<String>();
		$generatorsl = new ArrayList<Symbol>();
		$states = new ArrayList<String>();
	
	}
	;
	
prespec returns [String note, Symbol sgenerator, String state]: NOTE spec{
			$note = $NOTE.text;
			$sgenerator = $spec.sgenerator;
			$state = $spec.state;

		};

spec 	returns [Symbol sgenerator, String state]: WITH ID respec {
		$sgenerator = table.get($ID.text);
		if($sgenerator == null){

		}
		$state = $respec.state;
	} | {
		$sgenerator = SymbolTable.alwaysGenerating;
		$state = "";

	};

	
respec returns [String state]: DOT mvid{
		
		$state = $mvid.id;
		} | { $state = "";
		};


MODIFIERS
	: 'modifiers' | 'combinations';
SOUNDS  : 'sounds';
STATE 	: 'state';
PITCHES : 'is pitch bending';
SEMITONES 
	: 'semitone' | 'semitones';
STATES 	: 'states';
FROM	: 'from';
TO	: 'to';
AND	: 'and';
WITH	: 'with';
CONNECTED
	: 'connected to port';
CHANNEL : 'airs on channel' ;
ELEMS 	: 'elements' ;
GENERATORS 
	: 'generators' | 'graduals' | 'continuous' | 'analogs';
DEFAULT : 'default';
MONOPHONIC 
	: 'monophonic';
POLYPHONIC 
	: 'polyphonic';
INSTRUMENT
	:  'instrument';
PARI	: '(';
PARD	: ')';
COLON 	: ':';
DOT	: '.';
COMMA 	: ',';
AINT	: ('A'|'a')('0'..'9')+
	;
NOTE	: ('A'..'G'|'DO'|'RE'|'MI'|'FA'|'SOL'|'LA'|'SI'|'TI')('b'|'#')?('-1'|'0'..'9') | 'DRUM'('0'..'9')+;
ID  	: ('a'..'z'|'A'..'Z') ('a'..'z'|'A'..'Z'|'0'..'9')*;
INT 	: ('0'..'9')+
    	;
COMMENT
    :   '//' ~('\n'|'\r')* '\r'? '\n' -> skip;
WS  :   ( ' '
        | '\t'
        | '\r'
        | '\n'
        ) -> skip 
    ;
CHAR:  '\'' ( ESC_SEQ | ~('\''|'\\') ) '\''
    ;
fragment
HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ;
fragment

ESC_SEQ
    :   '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
    |   UNICODE_ESC
    |   OCTAL_ESC
    ;

fragment
OCTAL_ESC
    :   '\\' ('0'..'3') ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7')
    ;
fragment
UNICODE_ESC
    :   '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
    ;